package grafanax

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"time"

	"github.com/go-resty/resty/v2"
	"github.com/google/go-querystring/query"
)

type Config struct {
	Url          string
	AccountToken string
}

type DashboardManager struct {
	config Config
	client *resty.Client
}

type DashboardResp struct {
	Meta      any       `json:"meta"`
	Dashboard Dashboard `json:"dashboard"`
}

type DashboardUpdateReq struct {
	Dashboard Dashboard `json:"dashboard"`
	FolderUid string    `json:"folderUid"`
	Message   string    `json:"message"`
	Overwrite bool      `json:"overwrite"`
}

type DashboardUpdateResponse struct {
	ID      int    `json:"id"`
	UID     string `json:"uid"`
	URL     string `json:"url"`
	Status  string `json:"status"`
	Version int    `json:"version"`
	Slug    string `json:"slug"`
}

type DashboardSearchReq struct {
	Query         string   `url:"query,omitempty"`
	Tag           []string `url:"tag,omitempty"`
	Type          string   `url:"type,omitempty"`
	DashboardIds  []int    `url:"dashboardIds,omitempty"`
	DashboardUIDs []string `url:"dashboardUIDs,omitempty"`
	FolderIds     []int    `url:"folderIds,omitempty"`
	Starred       bool     `url:"starred,omitempty"`
	Limit         int      `url:"limit,omitempty"`
	Page          int      `url:"page,omitempty"`
}

func NewDashboardManager(config Config) *DashboardManager {
	restyClient := resty.New().
		SetTimeout(time.Second*10).
		EnableTrace().
		SetRetryCount(2).
		SetRetryWaitTime(500*time.Millisecond).
		SetAuthToken(config.AccountToken).
		SetHeader("Content-Type", "application/json")

	return &DashboardManager{
		config: config,
		client: restyClient,
	}
}

func (m *DashboardManager) GetDashboard(uid string) (*Dashboard, error) {
	url := m.config.Url + "/api/dashboards/uid/" + uid
	resp, err := m.client.R().SetResult(&DashboardResp{}).Get(url)
	// resp, err := m.client.R().Get(url)
	if err != nil {
		// prometheus.IncrClientTimeoutAwaitingHeaderError("pay-api", url, err.Error())
		return nil, fmt.Errorf("request error: %w", err)
	}

	if resp.StatusCode() != http.StatusOK {
		return nil, fmt.Errorf("resp failed status:%d,msg:%s", resp.StatusCode(), resp.Status())
	}

	// fmt.Println(string(resp.Body()))

	respData, ok := resp.Result().(*DashboardResp)
	if !ok {
		return nil, fmt.Errorf("request Result error:%v", resp.Result())
	}

	if respData == nil {
		return nil, errors.New("not found")
	}

	return &respData.Dashboard, nil
}

func (m *DashboardManager) UpdateDashboard(req DashboardUpdateReq) (*DashboardUpdateResponse, error) {
	url := m.config.Url + "/api/dashboards/db"

	data, err := json.Marshal(req)
	if err != nil {
		return nil, err
	}
	// fmt.Println(string(data))
	resp, err := m.client.R().SetBody(data).SetResult(&DashboardUpdateResponse{}).Post(url)
	if err != nil {
		// prometheus.IncrClientTimeoutAwaitingHeaderError("pay-api", url, err.Error())
		return nil, fmt.Errorf("request error: %w", err)
	}

	if resp.StatusCode() != http.StatusOK {
		return nil, fmt.Errorf("resp failed status:%d,msg:%s", resp.StatusCode(), resp.Status())
	}

	respData, ok := resp.Result().(*DashboardUpdateResponse)
	if !ok {
		return nil, fmt.Errorf("request Result error:%v", resp.Result())
	}

	if respData == nil {
		return nil, errors.New("not found")
	}

	return respData, nil
}

func (m *DashboardManager) SearchDashboards(ctx context.Context, req DashboardSearchReq) ([]Dashboard, error) {
	value, _ := query.Values(req)
	paramStr := value.Encode()
	resp, err := m.client.R().SetQueryString(paramStr).Get(m.config.Url + "/api/search")
	if err != nil {
		return nil, fmt.Errorf("request error: %w", err)
	}

	if resp.StatusCode() != http.StatusOK {
		return nil, fmt.Errorf("resp failed status:%d,msg:%s", resp.StatusCode(), resp.Status())
	}

	var dashboards []Dashboard
	err = json.Unmarshal(resp.Body(), &dashboards)
	if err != nil {
		return nil, fmt.Errorf("resp body error: %w data:%s", err, resp.Body())
	}

	// fmt.Println(string(resp.Body()))
	return dashboards, nil
}
